package com.epam.mycollections.sets;

import com.epam.mycollections.interfaces.MySet;

/**
 * My Implementation of the linked hash set data structure
 * @author Iurii Miedviediev
 * @version 1.0 Build 04.08.2014
 */
public class MyLinkedHashSet<E> extends MyHashSet<E> implements MySet<E> {

    private LinkedEntry<E> header;

    //MyLinkedHashSet constructors
    public MyLinkedHashSet() {
        super();
        header = new LinkedEntry<E>(null);
        header.before = header.after = header;
    }

    public MyLinkedHashSet(int initialCapacity) {
        super(initialCapacity);
        header = new LinkedEntry<E>(null);
        header.before = header.after = header;
    }

    public MyLinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        header = new LinkedEntry<E>(null);
        header.before = header.after = header;
    }
    //End of constructors

    /**
     * Entry of the LinkedHashSet, with links to prev and next elements
     * @param <E> any type of element
     */
    private static class LinkedEntry<E> {
        LinkedEntry<E> before;
        LinkedEntry<E> after;
        E element;

        public LinkedEntry(E element) {
            this.element = element;
        }

        public E getKey() {
            return element;
        }

        //add this entry before given in the linked list
        private void addBefore(LinkedEntry<E> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }

        //remove current entry from the linked list
        private void remove() {
            before.after = after;
            after.before = before;
        }
    }

    /**
     * Clear all elements from the set
     */
    @Override
    public void clear() {
        super.clear();
        header.before = header.after = header;
    }

    /**
     * Add new element to the set if there is no identical to it
     * @param element new element
     */
    @Override
    public void add(E element) {
        super.add(element);
        new LinkedEntry<E>(element).addBefore(header);
    }

    /**
     * Delete an element from the set
     * @param element element to delete
     * @return deleted element
     */
    @Override
    public E delete(E element) {
        E entry = super.delete(element);
        LinkedEntry<E> current = header.before;
        while(current.getKey() != null) {
            if(current.getKey().equals(element)) {
                current.remove();
                return entry;
            }
            current = current.before;
        }
        return entry;
    }

    /**
     * Convert linked hash set inti simple array
     * @return array of elements from linked hash set
     */
    public Object[] toArray() {
        int position = this.size()-1;
        Object[] result = new Object[position+1];
        LinkedEntry<E> currentEntry = header.before;
        while (currentEntry != header) {
            result[position] = currentEntry.getKey();
            position--;
            currentEntry = currentEntry.before;
        }
        return result;
    }
}